﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Net;
using System.IO;
using System.Web;

namespace OAuth
{
    public class OAuthBase
    {
        private IOAuthRequestURL ConfigRequestURL;
        private IOAuthConfig config;
        private IOAuthMode Mode;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="url">IOAuthRequestURL</param>
        /// <param name="_config">IOAuthConfig</param>
        public OAuthBase(IOAuthRequestURL url, IOAuthConfig _config, IOAuthMode _Mode)
        {
            ConfigRequestURL = url;
            config = _config;
            Mode = _Mode;
        }

        /// <summary>
        /// 请求AccessToken
        /// </summary>
        /// <returns></returns>
        public IOAuthMode RequestAccessToken()
        {
            Mode.SetOAuthNonce(OAuthCommon.GetOAuthNonce());
            Mode.SetOAuthTimeStamp(OAuthCommon.GetOAuthTimeStamp());
            string SignatureBaseString = GetSignatureBaseString(ConfigRequestURL.GetRequestAccessTokenURL(), OAuthEnum.OAuthRequestStatus.AccessTokenBase);
            string SignatureString = GetSignatureString(SignatureBaseString);
            Mode.SetOAuthSignature(SignatureString);
            string RequestURL = GetRequestTokenURL(ConfigRequestURL.GetRequestAccessTokenURL(), OAuthEnum.OAuthRequestStatus.AccessToken);
            GetOAuthToken(new Uri(RequestURL));
            return Mode;
        }

        /// <summary>
        /// 用词认证地址
        /// </summary>
        /// <returns></returns>
        public Uri GetAccessTokenURL()
        {
            OAuthParameter RequestOAuthToken = Mode.GetOAuthToken();
            Uri url = new Uri(ConfigRequestURL.GetRequestOAuthTokenURL(), "?" + RequestOAuthToken.ParameterName + "=" + RequestOAuthToken.ParameterValue);
            if (Mode.GetOAuthInterface() == OAuthEnum.OAuthInterface.NetEase)
            {
                OAuthParameter OAuthCallBack = Mode.GetOAuthCallBack();
                url = new Uri(ConfigRequestURL.GetRequestOAuthTokenURL(), "?" + RequestOAuthToken.ParameterName + "=" + RequestOAuthToken.ParameterValue + "&" + OAuthCallBack.ParameterName + "=" + OAuthCommon.UrlEncode(OAuthCallBack.ParameterValue));
            }
            return url;
        }

        /// <summary>
        /// 请求得到OAuthTokenMode
        /// </summary>
        /// <returns></returns>
        public IOAuthMode RequestToken()
        {
            string SignatureBaseString = GetSignatureBaseString(ConfigRequestURL.GetRequestTokenURL(), OAuthEnum.OAuthRequestStatus.RequestTokenBase);
            string SignatureString = GetSignatureString(SignatureBaseString);
            Mode.SetOAuthSignature(SignatureString);
            string RequestURL = GetRequestTokenURL(ConfigRequestURL.GetRequestTokenURL(), OAuthEnum.OAuthRequestStatus.RequestToken);
            GetOAuthToken(new Uri(RequestURL));
            return Mode;
        }

        /// <summary>
        /// GetOAuthToken
        /// </summary>
        /// <param name="RequestTokenURL"></param>
        /// <returns></returns>
        public void GetOAuthToken(Uri RequestTokenURL)
        {
            //StreamReader stream = GetStreamReader(RequestTokenURL);
            string responseBody = GetStreamReader(RequestTokenURL);
            string[] ParameArray = responseBody.Split("&".ToCharArray());
            string[] OAuthToken = ParameArray[0].Split("=".ToCharArray());
            Mode.SetOAuthToken(new OAuthParameter(OAuthToken[0], OAuthToken[1]));
            string[] OAuthTokenSecret = ParameArray[1].Split("=".ToCharArray());
            Mode.SetOAuthTokenSecret(new OAuthParameter(OAuthTokenSecret[0], OAuthTokenSecret[1]));
        }

        /// <summary>
        /// 得到请求流
        /// </summary>
        /// <param name="RequestURL"></param>
        /// <returns></returns>
        public string GetStreamReader(Uri RequestURL)
        {
            string responseData = null;
            HttpWebRequest webRequest = WebRequest.Create(RequestURL) as HttpWebRequest;
            webRequest.Method = "POST";
            webRequest.ServicePoint.Expect100Continue = false;
            webRequest.Timeout = 20000;
            StreamReader responseReader = null;
            try
            {
                responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream(), System.Text.Encoding.UTF8);
                responseData = responseReader.ReadToEnd();
            }
            catch(Exception ex)
            {
                throw new ApplicationException(ex.Message);
            }
            finally
            {
                webRequest.GetResponse().GetResponseStream().Close();
                responseReader.Close();
                responseReader = null;
                webRequest = null;
            }
            return responseData;
        }

        /// <summary>
        /// 返回请求URL
        /// </summary>
        /// <param name="RequestURL"></param>
        /// <param name="Status">请求状态</param>
        /// <returns></returns>
        public string GetRequestTokenURL(Uri RequestURL, OAuthEnum.OAuthRequestStatus Status)
        {
            return RequestURL.ToString() + GetParameString(Status);
        }

        /// <summary>
        /// 得到签名字符串
        /// </summary>
        /// <param name="SignatureBaseString">签名基字符串</param>
        /// <returns>得到签名字符串</returns>
        public string GetSignatureString(string SignatureBaseString)
        {
            HMACSHA1 hmacsha1 = new HMACSHA1();
            OAuthParameter OAuthTokenSecret = Mode.GetOAuthTokenSecret();
            string Key = string.Format("{0}&{1}", config.GetAppSecret(), OAuthTokenSecret == null ? "" : OAuthTokenSecret.ParameterValue);
            hmacsha1.Key = Encoding.ASCII.GetBytes(Key);
            HashAlgorithm hash = hmacsha1;
            byte[] dataBuffer = Encoding.ASCII.GetBytes(SignatureBaseString);
            byte[] hashBytes = hash.ComputeHash(dataBuffer);
            string oauth_signature = Convert.ToBase64String(hashBytes);
            return OAuthCommon.UrlEncode(oauth_signature);
        }

        /// <summary>
        /// 返回签名基字符串
        /// </summary>
        /// <param name="RequestURL">请求地址</param>
        /// <param name="Status">请求状态</param>
        /// <returns>签名基字符串</returns>
        public string GetSignatureBaseString(Uri RequestURL, OAuthEnum.OAuthRequestStatus Status)
        {
            string ParameString = GetParameString(Status).Substring(1);
            string SignatureString = string.Format("{0}&{1}&{2}", config.GetRequestType().ToString(), OAuthCommon.UrlEncode(RequestURL.ToString()), OAuthCommon.UrlEncode(ParameString));
            return SignatureString;
        }

        /// <summary>
        /// 返回参数字符串
        /// </summary>
        /// <param name="Status">请求状态</param>
        /// <returns></returns>
        public string GetParameString(OAuthEnum.OAuthRequestStatus Status)
        {
            List<OAuthParameter> ListPar = OAuthModeToList(Status);
            return GetParameString(ListPar);
        }

        /// <summary>
        /// 返回参数字符串
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        public static string GetParameString(List<OAuthParameter> par)
        {
            StringBuilder ParameString = new StringBuilder();
            for (int i = 0; i < par.Count; i++)
            {
                string formatString = i == 0 ? "?{0}={1}" : "&{0}={1}";
                ParameString.AppendFormat(formatString, par[i].ParameterName, par[i].ParameterValue);
            }
            return ParameString.ToString();
        }

        /// <summary>
        /// 把OAuthMode转化成List
        /// </summary>
        /// <param name="Status">请求状态</param>
        /// <returns></returns>
        public List<OAuthParameter> OAuthModeToList(OAuthEnum.OAuthRequestStatus Status)
        {
            List<OAuthParameter> Parame = new List<OAuthParameter>();
            if (Status == OAuthEnum.OAuthRequestStatus.RequestTokenBase || Status == OAuthEnum.OAuthRequestStatus.RequestToken)
            {
                OAuthParameter OAuthCallBack = Mode.GetOAuthCallBack();
                OAuthCallBack.ParameterValue = OAuthCommon.UrlEncode(OAuthCallBack.ParameterValue);
                Parame.Add(OAuthCallBack);
            }
            Parame.Add(Mode.GetOAuthConsumerKey());
            Parame.Add(Mode.GetOAuthNonce());
            Parame.Add(Mode.GetOAuthSignatureMethod());
            Parame.Add(Mode.GetOAuthTimeStamp());
            Parame.Add(Mode.GetOAuthVersion());
            if (Status == OAuthEnum.OAuthRequestStatus.AccessToken || Status == OAuthEnum.OAuthRequestStatus.RequestToken)
            {
                Parame.Add(Mode.GetOAuthSignature());
            }
            if (Status == OAuthEnum.OAuthRequestStatus.AccessToken || Status == OAuthEnum.OAuthRequestStatus.AccessTokenBase)
            {
                Parame.Add(Mode.GetOAuthToken());
                Parame.Add(Mode.GetOAuthVerifier());

            }
            Parame.Sort(new OAuthParameterCompre());
            return Parame;
        }
    }
}
